package components;

import java.util.ArrayList;
import java.util.List;

import com.uqbar.vainilla.DeltaState;

public class ComponentsHashTable<CC extends CollisionableComponent<?, CC>> {


	
	private int width;
	private int height;
	private int quadrantSize;
	
	private List<CC>[][] data;
	
	public ComponentsHashTable (int qsize, int widht, int height){
		this.width = widht;
		this.height = height;
		this.quadrantSize = qsize;
		
		this.initData();
		
	}

	private void initData() {
		int xsq, ysq;
		xsq = this.width/this.quadrantSize;
		ysq = this.height/this.quadrantSize;
		
		data = new ArrayList[xsq][ysq];
		
		
		for (int i = 0; i < ysq; i++){
			for (int ii = 0; ii < xsq; ii++){
				data[ii][i] = new ArrayList<CC>();
			}
		}
		
	}
	
	public void addCollisionableComponent(CC gc, double possX, double possY){
		int iterX, iterY, startX , startY;
		iterX = (int) Math.ceil(gc.getDiameter()/this.quadrantSize);
		iterY = (int) Math.ceil(gc.getHeight()/this.quadrantSize);
		
		
		if (Math.floor(possX/this.quadrantSize) < Math.floor((gc.getDiameter() + possX) / this.quadrantSize )){
			iterX++;
		}
		if (Math.floor(possY/this.quadrantSize) < Math.floor((gc.getHeight() + possY) / this.quadrantSize )){
			iterY++;
		}
		
		startX = (int) possX/quadrantSize;
		startY = (int) possY/quadrantSize;
		
		if (iterY + startY > this.height/quadrantSize){
			iterY--;
		}
		if (iterX + startX > this.width/quadrantSize){
			iterX--;
		}

		int xtimes, ytimes;
		ytimes = 0;
		xtimes = 0;
				
		for (int i = startY; ytimes < iterY; i++){
			for (int ii = startX; xtimes < iterX; ii++){

				data[ii][i].add(gc);
				xtimes++;
			}
			xtimes = 0;
			ytimes++;
		}
	}
	public void remove(CC gc, double possX, double possY){
		
		if (!this.outOfBounds(possX, possY)){
			double gcw, gch;
			gcw =  gc.getDiameter();
			gch =  gc.getHeight();
			
			int startX, startY, iterX, iterY;
			
			startX = (int)possX/quadrantSize;
			startY = (int)possY/quadrantSize;
			
			iterX = (int) Math.ceil(gcw/this.quadrantSize);
			iterY = (int) Math.ceil(gch/this.quadrantSize);


			if (Math.floor(possX/this.quadrantSize) < Math.floor((gcw + possX) / this.quadrantSize )){
				if(!this.outOfQuadrantX(iterX +1)){
					iterX++;
				}
			}
			if (Math.floor(possY/this.quadrantSize) < Math.floor((gch + possY) / this.quadrantSize )){
				if(!this.outOfQuadrantY(iterY +1)){
					iterY++;
				}
			}

			int xc, yc;
			xc = 0; yc = 0;

			for (int i = startY; yc < iterY & i <= this.lastYquadrant(); i++){
				for (int ii = startX; xc < iterX & ii <= this.lastXquadrant(); ii++){
					this.data[ii][i].remove(gc);
					}
					xc++;
				xc = 0;
				yc++;
			}
		}
		

	}

	public List<CC> getComponentsNearSectorExcluding(CC exclude, double x, double y, int width, int height){
		List<CC > res = new ArrayList<CC>();
		if (this.outOfBounds(x, y)){
			return res;
		}
		
		int startX, startY, iterX, iterY;
		startX = (int)x/quadrantSize;
		startY = (int)y/quadrantSize;
		iterX = 1 +(width/quadrantSize);
		iterY = 1 +(height/quadrantSize);

		
		
		if (Math.floor(x/this.quadrantSize) < Math.floor((width + x) / this.quadrantSize )){
			if(!this.outOfQuadrantX(iterX +1)){
				iterX++;
			}
		}
		if (Math.floor(y/this.quadrantSize) < Math.floor((height + y) / this.quadrantSize )){
			if(!this.outOfQuadrantY(iterY +1)){
				iterY++;
			}
		}
		
		int xc, yc;
		xc = 0; yc = 0;

		for (int i = startY; yc < iterY & i <= this.lastYquadrant(); i++){
			for (int ii = startX; xc < iterX & ii <= this.lastXquadrant(); ii++){
				res.addAll(this.data[ii][i]);
				if (res.contains(exclude)){
					res.remove(exclude);
				}
				xc++;
			}
			xc = 0;
			yc++;
		}
		return res;
	}

	
	public void move(CC gc, double fromX, double fromY, double toX, double toY){
		//remove
		this.remove(gc, fromX, fromY);
		
		//add
		this.addCollisionableComponent(gc, toX, toY);
		
	}
	
	public String toString(){
		String st = "";
		for (int i = 0; i < this.width/quadrantSize ;i++){
			for (int ii = 0; ii < this.height/quadrantSize ; ii++){
				st += "[" + data[i][ii].size() + "]";
			}
			st += "\n";
		}
		return st;
		
	}
	public void print(){
		
	}
	public boolean outOfBounds(double x, double y){
		return x < 0 | x >= this.width | y < 0 | y >= this.height;
	}
	public boolean outOfQuadrantX(int x){
		return x < 0 | x >= this.width/this.quadrantSize;
	}
	public boolean outOfQuadrantY(int y){
		return y < 0 | y >= this.height/this.quadrantSize;
	}
	public int lastXquadrant(){
		return this.width / this.quadrantSize -1;
	}
	public int lastYquadrant(){
		return this.height / this.quadrantSize -1;
	}

	public boolean nonCollisionOn(double x, double y, CC comp) {
		double oldX, oldY;
		oldX = comp.getX();
		oldY = comp.getY();
		comp.setX(x);
		comp.setY(y);
		
		List<CC> ccs = this.getComponentsNearSectorExcluding(comp, x, y, comp.getDiameter(), comp.getHeight());

		for (CC cc : ccs){
			if (comp.collisionNature(cc)){
				comp.setX(oldX);
				comp.setY(oldY);
				return false;
			}
		}
		comp.setX(oldX);
		comp.setY(oldY);
		return true;
	}


}
